package service

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
	"go-file-perception-model/internal/config"
	"go-file-perception-model/internal/logger"
	"go-file-perception-model/internal/model"
	"go-file-perception-model/internal/repository"
	"go-file-perception-model/pkg/vector"
)

// FileMonitor 文件监控器
type FileMonitor struct {
	indexManager    *IndexManager
	directoryMgr    *DirectoryManager
	fileMonitorRepo repository.FileMonitorRepository
	config          *config.FileIndexConfig
	isRunning       bool
	stopChan        chan struct{}
	mutex           sync.RWMutex
}

// NewFileMonitor 创建文件监控器实例
func NewFileMonitor(indexManager *IndexManager, directoryMgr *DirectoryManager, fileMonitorRepo repository.FileMonitorRepository, cfg *config.FileIndexConfig) *FileMonitor {
	return &FileMonitor{
		indexManager:    indexManager,
		directoryMgr:    directoryMgr,
		fileMonitorRepo: fileMonitorRepo,
		config:          cfg,
		stopChan:        make(chan struct{}),
	}
}

// StartFileMonitoring 启动文件监控
func (m *FileMonitor) StartFileMonitoring() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if m.isRunning {
		return fmt.Errorf("file monitoring is already running")
	}

	m.isRunning = true
	
	// 启动监控goroutine
	go m.monitorLoop()
	
	logger.Info("File monitoring started")
	return nil
}

// StopFileMonitoring 停止文件监控
func (m *FileMonitor) StopFileMonitoring() error {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	if !m.isRunning {
		return fmt.Errorf("file monitoring is not running")
	}

	close(m.stopChan)
	m.isRunning = false
	
	logger.Info("File monitoring stopped")
	return nil
}

// IsRunning 检查监控是否正在运行
func (m *FileMonitor) IsRunning() bool {
	m.mutex.RLock()
	defer m.mutex.RUnlock()
	return m.isRunning
}

// monitorLoop 监控循环
func (m *FileMonitor) monitorLoop() {
	// 首次启动时立即检查一次
	m.checkForChanges()
	
	// 根据配置的间隔时间进行定期检查
	interval := time.Duration(300) * time.Second // 默认5分钟
	ticker := time.NewTicker(interval)
	defer ticker.Stop()

	for {
		select {
		case <-m.stopChan:
			return
		case <-ticker.C:
			m.checkForChanges()
		}
	}
}

// checkForChanges 检查文件变更
func (m *FileMonitor) checkForChanges() {
	logger.Info("Checking for file changes...")

	// 遍历配置的监控目录
	for _, dir := range m.config.IndexDirectories {
		if err := m.scanDirectoryForChanges(dir); err != nil {
			logger.Error("Failed to scan directory for changes", zap.String("directory", dir), zap.Error(err))
		}
	}
	
	// 处理检测到的文件变更
	m.processFileChanges()
}

// scanDirectoryForChanges 扫描目录变更
func (m *FileMonitor) scanDirectoryForChanges(dir string) error {
	// 确保目录已注册
	directoryRecord, err := m.directoryMgr.RegisterDirectory(dir)
	if err != nil {
		return fmt.Errorf("failed to register directory: %w", err)
	}

	// 更新目录最后扫描时间
	if err := m.directoryMgr.UpdateDirectoryLastScanTime(dir); err != nil {
		logger.Error("Failed to update directory last scan time", zap.String("directory", dir), zap.Error(err))
	}

	// 获取目录ID
	directoryID := directoryRecord.ID

	// 获取目录中已知的文件
	knownFiles, err := m.fileMonitorRepo.GetFileRecordsByDirectoryID(directoryID)
	if err != nil {
		return fmt.Errorf("failed to get known files: %w", err)
	}

	// 创建已知文件的映射
	knownFileMap := make(map[string]*model.FileMonitorRecord)
	for _, file := range knownFiles {
		knownFileMap[file.FilePath] = file
	}

	// 扫描目录中的所有文件
	currentFiles := make(map[string]os.FileInfo)
	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		// 跳过目录
		if info.IsDir() {
			return nil
		}

		// 检查文件扩展名是否支持
		if !m.isSupportedFile(path) {
			return nil
		}

		currentFiles[path] = info
		return nil
	})
	if err != nil {
		return fmt.Errorf("failed to walk directory: %w", err)
	}

	// 检查新增和修改的文件
	for filePath, fileInfo := range currentFiles {
		knownFile, exists := knownFileMap[filePath]
		
		if !exists {
			// 新增文件
			if err := m.handleFileAdded(directoryID, filePath, fileInfo); err != nil {
				logger.Error("Failed to handle added file", zap.String("file", filePath), zap.Error(err))
			}
		} else {
			// 检查文件是否被修改
			if m.isFileModified(knownFile, fileInfo) {
				if err := m.handleFileModified(directoryID, filePath, fileInfo, knownFile); err != nil {
					logger.Error("Failed to handle modified file", zap.String("file", filePath), zap.Error(err))
				}
			}
		}
	}

	// 检查删除的文件
	for filePath, knownFile := range knownFileMap {
		if _, exists := currentFiles[filePath]; !exists {
			if err := m.handleFileDeleted(directoryID, filePath, knownFile); err != nil {
				logger.Error("Failed to handle deleted file", zap.String("file", filePath), zap.Error(err))
			}
		}
	}

	return nil
}

// isSupportedFile 检查文件是否支持
func (m *FileMonitor) isSupportedFile(filePath string) bool {
	ext := strings.ToLower(filepath.Ext(filePath))
	for _, supportedExt := range m.config.SupportedExtensions {
		if ext == strings.ToLower(supportedExt) {
			return true
		}
	}
	return false
}

// isFileModified 检查文件是否被修改
func (m *FileMonitor) isFileModified(knownFile *model.FileMonitorRecord, currentInfo os.FileInfo) bool {
	// 检查文件大小
	if knownFile.FileSize != currentInfo.Size() {
		return true
	}
	
	// 检查修改时间
	if !knownFile.FileModTime.Equal(currentInfo.ModTime()) {
		return true
	}
	
	// 如果需要更精确的检查，可以计算文件哈希
	// 这里为了性能，只检查大小和修改时间
	return false
}

// handleFileAdded 处理新增文件
func (m *FileMonitor) handleFileAdded(directoryID, filePath string, fileInfo os.FileInfo) error {
	logger.Info("File added", zap.String("file", filePath))
	
	// 计算文件哈希
	fileHash, err := m.calculateFileHash(filePath)
	if err != nil {
		return fmt.Errorf("failed to calculate file hash: %w", err)
	}
	
	// 创建文件记录
	now := time.Now()
	fileRecord := &model.FileMonitorRecord{
		ID:          uuid.New().String(),
		DirectoryID: directoryID,
		FilePath:    filePath,
		FileName:    filepath.Base(filePath),
		FileSize:    fileInfo.Size(),
		FileModTime: fileInfo.ModTime(),
		FileHash:    fileHash,
		IsIndexed:   false,
		CreatedAt:   now,
		UpdatedAt:   now,
	}
	
	// 保存文件记录
	if err := m.fileMonitorRepo.CreateFileRecord(fileRecord); err != nil {
		return fmt.Errorf("failed to create file record: %w", err)
	}
	
	// 创建文件变更记录
	change := &model.FileMonitorChange{
		ID:          uuid.New().String(),
		DirectoryID: directoryID,
		FilePath:    filePath,
		ChangeType:  model.FileChangeType("added"),
		NewSize:     fileInfo.Size(),
		NewModTime:  fileInfo.ModTime(),
		NewHash:     fileHash,
		Processed:   false,
		CreatedAt:   now,
	}
	
	return m.fileMonitorRepo.CreateFileChange(change)
}

// handleFileModified 处理修改的文件
func (m *FileMonitor) handleFileModified(directoryID, filePath string, fileInfo os.FileInfo, knownFile *model.FileMonitorRecord) error {
	logger.Info("File modified", zap.String("file", filePath))
	
	// 计算文件哈希
	fileHash, err := m.calculateFileHash(filePath)
	if err != nil {
		return fmt.Errorf("failed to calculate file hash: %w", err)
	}
	
	// 更新文件记录
	knownFile.FileSize = fileInfo.Size()
	knownFile.FileModTime = fileInfo.ModTime()
	knownFile.FileHash = fileHash
	knownFile.IsIndexed = false
	knownFile.UpdatedAt = time.Now()
	
	if err := m.fileMonitorRepo.UpdateFileRecord(knownFile); err != nil {
		return fmt.Errorf("failed to update file record: %w", err)
	}
	
	// 创建文件变更记录
	change := &model.FileMonitorChange{
		ID:          uuid.New().String(),
		DirectoryID: directoryID,
		FilePath:    filePath,
		ChangeType:  model.FileChangeType("modified"),
		OldSize:     knownFile.FileSize,
		NewSize:     fileInfo.Size(),
		OldModTime:  knownFile.FileModTime,
		NewModTime:  fileInfo.ModTime(),
		OldHash:     knownFile.FileHash,
		NewHash:     fileHash,
		Processed:   false,
		CreatedAt:   time.Now(),
	}
	
	return m.fileMonitorRepo.CreateFileChange(change)
}

// handleFileDeleted 处理删除的文件
func (m *FileMonitor) handleFileDeleted(directoryID, filePath string, knownFile *model.FileMonitorRecord) error {
	logger.Info("File deleted", zap.String("file", filePath))
	
	// 删除文件记录
	if err := m.fileMonitorRepo.DeleteFileRecord(knownFile.ID); err != nil {
		return fmt.Errorf("failed to delete file record: %w", err)
	}
	
	// 创建文件变更记录
	change := &model.FileMonitorChange{
		ID:          uuid.New().String(),
		DirectoryID: directoryID,
		FilePath:    filePath,
		ChangeType:  model.FileChangeType("deleted"),
		OldSize:     knownFile.FileSize,
		OldModTime:  knownFile.FileModTime,
		OldHash:     knownFile.FileHash,
		Processed:   false,
		CreatedAt:   time.Now(),
	}
	
	return m.fileMonitorRepo.CreateFileChange(change)
}

// calculateFileHash 计算文件哈希
func (m *FileMonitor) calculateFileHash(filePath string) (string, error) {
	file, err := os.Open(filePath)
	if err != nil {
		return "", err
	}
	defer file.Close()
	
	hasher := md5.New()
	if _, err := io.Copy(hasher, file); err != nil {
		return "", err
	}
	
	hashBytes := hasher.Sum(nil)
	return hex.EncodeToString(hashBytes), nil
}

// processFileChanges 处理文件变更
func (m *FileMonitor) processFileChanges() {
	// 获取所有未处理的文件变更
	changes, err := m.fileMonitorRepo.GetUnprocessedFileChanges()
	if err != nil {
		logger.Error("Failed to get unprocessed file changes", zap.Error(err))
		return
	}
	
	if len(changes) == 0 {
		return
	}
	
	logger.Info("Processing file changes", zap.Int("count", len(changes)))
	
	// 按目录分组处理变更
	changesByDir := make(map[string][]*model.FileMonitorChange)
	for _, change := range changes {
		changesByDir[change.DirectoryID] = append(changesByDir[change.DirectoryID], change)
	}
	
	// 处理每个目录的变更
	for directoryID, dirChanges := range changesByDir {
		if err := m.processDirectoryChanges(directoryID, dirChanges); err != nil {
			logger.Error("Failed to process directory changes", zap.String("directory_id", directoryID), zap.Error(err))
		}
	}
}

// processDirectoryChanges 处理目录变更
func (m *FileMonitor) processDirectoryChanges(directoryID string, changes []*model.FileMonitorChange) error {
	// 获取目录信息
	records, err := m.fileMonitorRepo.ListAllDirectoryRecords()
	if err != nil {
		return fmt.Errorf("failed to list directory records: %w", err)
	}
	
	var directoryPath string
	for _, record := range records {
		if record.ID == directoryID {
			directoryPath = record.DirectoryPath
			break
		}
	}
	
	if directoryPath == "" {
		return fmt.Errorf("directory not found: %s", directoryID)
	}
	
	// 设置活动目录
	if err := m.directoryMgr.SetActiveDirectory(directoryPath); err != nil {
		return fmt.Errorf("failed to set active directory: %w", err)
	}
	
	// 处理每个变更
	for _, change := range changes {
		if err := m.processSingleChange(change); err != nil {
			logger.Error("Failed to process file change", 
				zap.String("file", change.FilePath), 
				zap.String("change_type", string(change.ChangeType)), 
				zap.Error(err))
			continue
		}
		
		// 标记变更已处理
		now := time.Now()
		change.Processed = true
		change.ProcessedAt = now
		if err := m.fileMonitorRepo.UpdateFileChange(change); err != nil {
			logger.Error("Failed to update file change status", zap.String("change_id", change.ID), zap.Error(err))
		}
	}
	
	return nil
}

// processSingleChange 处理单个变更
func (m *FileMonitor) processSingleChange(change *model.FileMonitorChange) error {
	switch change.ChangeType {
	case model.FileChangeType("added"):
		// 处理新增文件
		return m.processAddedFile(change)
	case model.FileChangeType("modified"):
		// 处理修改的文件
		return m.processModifiedFile(change)
	case model.FileChangeType("deleted"):
		// 处理删除的文件
		return m.processDeletedFile(change)
	default:
		return fmt.Errorf("unknown change type: %s", change.ChangeType)
	}
}

// processAddedFile 处理新增文件
func (m *FileMonitor) processAddedFile(change *model.FileMonitorChange) error {
	// 使用索引管理器处理文件
	if err := m.indexManager.ProcessFile(change.FilePath); err != nil {
		return fmt.Errorf("failed to process added file: %w", err)
	}
	
	// 更新文件记录状态为已索引
	fileRecord, err := m.fileMonitorRepo.GetFileRecordByPath(change.DirectoryID, change.FilePath)
	if err != nil {
		return fmt.Errorf("failed to get file record: %w", err)
	}
	
	if fileRecord != nil {
		fileRecord.IsIndexed = true
		fileRecord.IndexedAt = time.Now()
		if err := m.fileMonitorRepo.UpdateFileRecord(fileRecord); err != nil {
			return fmt.Errorf("failed to update file record: %w", err)
		}
	}
	
	return nil
}

// processModifiedFile 处理修改的文件
func (m *FileMonitor) processModifiedFile(change *model.FileMonitorChange) error {
	// 首先删除旧的索引
	filter := &vector.Filter{
		Must: []vector.Condition{
			{
				Key:   "file_path",
				Match: &vector.MatchValue{Value: change.FilePath},
			},
		},
	}
	
	if err := m.indexManager.vectorDB.DeletePointsByFilter(context.Background(), filter); err != nil {
		return fmt.Errorf("failed to delete old file index: %w", err)
	}
	
	// 重新索引文件
	if err := m.indexManager.ProcessFile(change.FilePath); err != nil {
		return fmt.Errorf("failed to reindex modified file: %w", err)
	}
	
	// 更新文件记录状态为已索引
	fileRecord, err := m.fileMonitorRepo.GetFileRecordByPath(change.DirectoryID, change.FilePath)
	if err != nil {
		return fmt.Errorf("failed to get file record: %w", err)
	}
	
	if fileRecord != nil {
		fileRecord.IsIndexed = true
		fileRecord.IndexedAt = time.Now()
		if err := m.fileMonitorRepo.UpdateFileRecord(fileRecord); err != nil {
			return fmt.Errorf("failed to update file record: %w", err)
		}
	}
	
	return nil
}

// processDeletedFile 处理删除的文件
func (m *FileMonitor) processDeletedFile(change *model.FileMonitorChange) error {
	// 删除文件索引
	filter := &vector.Filter{
		Must: []vector.Condition{
			{
				Key:   "file_path",
				Match: &vector.MatchValue{Value: change.FilePath},
			},
		},
	}
	
	if err := m.indexManager.vectorDB.DeletePointsByFilter(context.Background(), filter); err != nil {
		return fmt.Errorf("failed to delete file index: %w", err)
	}
	
	return nil
}

// GetMonitoringStatus 获取监控状态
func (m *FileMonitor) GetMonitoringStatus() map[string]interface{} {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	return map[string]interface{}{
		"is_running":           m.isRunning,
		"monitored_directories": m.config.IndexDirectories,
		"auto_index_enabled":   m.config.AutoIndexNewFiles,
		"scan_interval":        300, // 默认5分钟
	}
}